/*
 * Copyright (c) 2021 Talkweb Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <unistd.h>
#include "stdio.h"
#include "ohos_init.h"
#include "cmsis_os2.h"

#include "hi_i2c.h"
#include "hi_io.h"
#include "hi_errno.h"
#include "iot_gpio.h"
#include "iot_i2c.h"
#include "iot_adc.h"
#include "oledfont.h"

#define OLED_TASK_STACK_SIZE 4096
#define OLED_TASK_PRIO 25    

#define OLED_CMD  0	 //写命令
#define OLED_DATA 1	 //写数据

osThreadId_t g_oledTaskID = NULL;
unsigned char OLED_GRAM[144][8];
unsigned char g_send_data[2] = { 0 };

/**********************************************
// IIC 写入1个字节
**********************************************/
unsigned int my_i2c_write(hi_i2c_idx id, unsigned short device_addr, unsigned int send_len)
{
    unsigned int status;
    hi_i2c_data es8311_i2c_data = { 0 };

    es8311_i2c_data.send_buf = g_send_data;
    es8311_i2c_data.send_len = send_len;
    status = hi_i2c_write(id, device_addr, &es8311_i2c_data);
    if (status != HI_ERR_SUCCESS) {
        printf("===== Error: I2C write status = 0x%x! =====\r\n", status);
        return status;
    }

    return HI_ERR_SUCCESS;
}

/**********************************************
// IIC 写命令
**********************************************/
void Write_IIC_Command(unsigned char IIC_Command)
{
    g_send_data[0] = 0x00;
    g_send_data[1] = IIC_Command;

    my_i2c_write(HI_I2C_IDX_0, 0x78, 2);
}

/**********************************************
// IIC 写数据
**********************************************/
void Write_IIC_Data(unsigned char IIC_Data)
{
    g_send_data[0] = 0x40;
    g_send_data[1] = IIC_Data;

    my_i2c_write(HI_I2C_IDX_0, 0x78, 2);
}

/**********************************************
// IIC 写函数，包括数据和命令
**********************************************/
void OLED_WR_Byte(unsigned char dat,unsigned char cmd)
{
	if(cmd == OLED_CMD) {
       Write_IIC_Data(dat);
    }
	else {
       Write_IIC_Command(dat);	
	}
}

/**********************************************
// 配置写入数据的起始位置
**********************************************/
void OLED_WR_BP(unsigned char x,unsigned char y)
{
	OLED_WR_Byte(0xb0+y,OLED_CMD);//设置行起始地址
	OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
	OLED_WR_Byte((x&0x0f),OLED_CMD);
}

/**********************************************
// 开启OLED显示
**********************************************/ 
void OLED_DisPlay_On(void)
{
	OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
	OLED_WR_Byte(0x14,OLED_CMD);//开启电荷泵
	OLED_WR_Byte(0xAF,OLED_CMD);//点亮屏幕
}

/**********************************************
// 关闭OLED显示
**********************************************/ 
void OLED_DisPlay_Off(void)
{
	OLED_WR_Byte(0x8D,OLED_CMD);//电荷泵使能
	OLED_WR_Byte(0x10,OLED_CMD);//关闭电荷泵
	OLED_WR_Byte(0xAF,OLED_CMD);//关闭屏幕
}

/**********************************************
// 更新显存到OLED
**********************************************/ 
void OLED_Refresh(void)
{
	unsigned char  i,n;
	for(i=0;i<8;i++)
	{
	   OLED_WR_Byte(0xb0+i,OLED_CMD); //设置行起始地址
	   OLED_WR_Byte(0x00,OLED_CMD);   //设置低列起始地址
	   OLED_WR_Byte(0x10,OLED_CMD);   //设置高列起始地址
	   for(n=0;n<128;n++)
		 OLED_WR_Byte(OLED_GRAM[n][i],OLED_DATA);
    }
}

/**********************************************
// 清屏函数
**********************************************/ 
void OLED_Clear(void)
{
	unsigned char  i,n;
	for(i=0;i<8;i++)
	{
	   for(n=0;n<128;n++)
		{
			OLED_GRAM[n][i]=0;//清除所有数据
		}
    }
  	OLED_Refresh();//更新显示
}

/**********************************************
// 画点函数
//x:0~127
//y:0~63
**********************************************/ 
void OLED_DrawPoint(unsigned char x,unsigned char y)
{
	unsigned char  i,m,n;
	i=y/8;
	m=y%8;
	n=1<<m;
	OLED_GRAM[x][i]|=n;
}

/**********************************************
//清除一个点
//x:0~127
//y:0~63
**********************************************/ 
void OLED_ClearPoint(unsigned char x,unsigned char y)
{
	unsigned char  i,m,n;
	i=y/8;
	m=y%8;
	n=1<<m;
	OLED_GRAM[x][i]=~OLED_GRAM[x][i];
	OLED_GRAM[x][i]|=n;
	OLED_GRAM[x][i]=~OLED_GRAM[x][i];
}

/**********************************************
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//size:选择字体 12/16/24
//取模方式 逐列式
**********************************************/ 
void OLED_ShowChar(unsigned char x,unsigned char y,unsigned char chr,unsigned char size1)
{
	unsigned char  i,m,temp,size2,chr1;
	unsigned char  y0=y;
	size2=(size1/8+((size1%8)?1:0))*(size1/2);  //得到字体一个字符对应点阵集所占的字节数
	chr1=chr-' ';  //计算偏移后的值
	for(i=0;i<size2;i++)
	{
		//temp=asc2_1206[chr1][i];
		if(size1==12)
        {temp=asc2_1206[chr1][i];} //调用1206字体
		else if(size1==16)
        {temp=asc2_1608[chr1][i];} //调用1608字体
		else return;

        for(m=0;m<8;m++)           //写入数据
        {
            if(temp&0x80)OLED_DrawPoint(x,y);
            else OLED_ClearPoint(x,y);
            temp<<=1;
            y++;
            if((y-y0)==size1)
            {
                y=y0;
                x++;
                break;
            }
		}
  }
}

/**********************************************
//显示字符串
//x,y:起点坐标  
//size1:字体大小 
//*chr:字符串起始地址 
**********************************************/ 
void OLED_ShowString(unsigned char x,unsigned char y,char *chr,unsigned char size1)
{
	while((*chr>=' ')&&(*chr<='~'))//判断是不是非法字符!
	{
		OLED_ShowChar(x,y,*chr,size1);
		x+=size1/2;
		if(x>128-size1)  //换行
		{
			x=0;
			y+=2;
    }
		chr++;
  }
}

/**********************************************
// oled初始化设置函数，用于设置oled的一些基本参数信息，具体参考芯片手册
**********************************************/ 
static void oled_init(void)
{
    OLED_WR_Byte(0xAE,OLED_CMD);//--turn off oled panel  #
	OLED_WR_Byte(0x00,OLED_CMD);//---set low column address #
	OLED_WR_Byte(0x10,OLED_CMD);//---set high column address #
	OLED_WR_Byte(0x40,OLED_CMD);//--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F) #
	OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register #
	OLED_WR_Byte(0xCF,OLED_CMD);// Set SEG Output Current Brightness #
	OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping     0xa0左右反置 0xa1正常 #
	OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction   0xc0上下反置 0xc8正常 #
	OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
	OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64) #
	OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty #
	OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset	Shift Mapping RAM Counter (0x00~0x3F) #
	OLED_WR_Byte(0x00,OLED_CMD);//-not offset #
	OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency #
	OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec #
	OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period #
	OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock #
	OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration #
	OLED_WR_Byte(0x12,OLED_CMD); // #
	OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh #
	OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level #
	OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02) #
	OLED_WR_Byte(0x02,OLED_CMD);//
	OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable #
	OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable #
	OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5) #
	OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7)  #
	OLED_WR_Byte(0xAF,OLED_CMD);// #
	OLED_Clear();
}

static void oledDemoTask(void)
{
	int ret;

    //初始化I2C0 配置，速率为400K
	IoTGpioInit(IOT_GPIO_IO_GPIO_13);
	IoTGpioInit(IOT_GPIO_IO_GPIO_14);

    IotIoSetPull(IOT_GPIO_IO_GPIO_13, IOT_IO_PULL_UP);
    IotIoSetPull(IOT_GPIO_IO_GPIO_14, IOT_IO_PULL_UP);

	IotIoSetFunc(IOT_GPIO_IO_GPIO_13,HI_IO_FUNC_GPIO_13_I2C0_SDA);
	IotIoSetFunc(IOT_GPIO_IO_GPIO_14,HI_IO_FUNC_GPIO_14_I2C0_SCL);

    hi_i2c_init(HI_I2C_IDX_0, 400*1000); 

	//oled初始化配置
    oled_init();

	//显示字符串
	OLED_ShowString(10,24,"Hello Harmony",16);
	OLED_Refresh();

	//删除任务
	osThreadTerminate(g_oledTaskID);
}

static void oledDemoEntry(void)
{
    osThreadAttr_t attr;
    attr.name = "oledDemoTask";
    attr.attr_bits = 0U;
    attr.cb_mem = NULL;
    attr.cb_size = 0U;
    attr.stack_mem = NULL;
    attr.stack_size = OLED_TASK_STACK_SIZE;
    attr.priority = OLED_TASK_PRIO;

	g_oledTaskID = osThreadNew((osThreadFunc_t)oledDemoTask, NULL, &attr);
    if (g_oledTaskID == NULL) {
        printf("[oledDemoExample] Falied to create oledDemoTask!\n");
    }
	return;
}

SYS_RUN(oledDemoEntry);

